package org.swift.commons.xml.vtd;

import java.io.ByteArrayOutputStream;
import java.util.Map;

import org.swift.commons.xml.Match;
import org.swift.commons.xml.ValueMatch;
import org.swift.commons.xml.XmlParseException;

import com.ximpleware.AutoPilot;
import com.ximpleware.NavException;
import com.ximpleware.VTDGen;
import com.ximpleware.VTDNav;


public class MemoryNavigator implements Navigator {
	
	private final VTDGen gen;
	private final VTDNav nav;
	private final byte[] xml;
	
	public MemoryNavigator(byte[] xml) throws XmlParseException {
		this.gen = new VTDGen();
		gen.setDoc(xml);
		this.xml = xml;
		try {
			gen.parse(true);
		} catch (Exception e) {
			throw new XmlParseException("Error while parsing xml document", e);
		} 
		this.nav = gen.getNav();
	}

	@Override
	public AutomaticPilot getAutomaticPilot() {
		return new MemoryAutomaticPilot(
				new AutoPilot(this.nav));
	}

	@Override
	public int getAttrVal(String string) throws NavigationException {
		try {
			return this.nav.getAttrVal(string);
		} catch (NavException e) {
			throw new NavigationException(e);
		}
	}
	
	public long getElementFragment() throws NavigationException {
		try {
			return this.nav.getElementFragment();
		} catch (NavException e) {
			throw new NavigationException(e);
		}
	}

	@Override
	public int getText() {
		return this.nav.getText();
	}

	@Override
	public int getTokenType(int index) {
		return this.nav.getTokenType(index);
	}

	@Override
	public String toString(int index) throws NavigationException {
		try {
			return this.nav.toString(index);
		} catch (NavException e) {
			throw new NavigationException(e);
		}
	}

	@Override
	public Match matchAt(int index, Map<String, String> namespaces) throws NavigationException {		
		int type = nav.getTokenType(index);
		try {
			switch (type) {
				case VTDNav.TOKEN_STARTING_TAG :
						//fast, but you lose the namespaces				
						long location = nav.getElementFragment();					
						int len = (int) (location>>32);
				        int offset = (int) location;			        
				        ByteArrayOutputStream out = new ByteArrayOutputStream();
				        out.write(this.xml, offset, len);			        
				        return new IndexedMatch(this.xml, offset, len, namespaces);
				        
						//return new String(nav.getElementFragmentNs().toBytes());					
				case VTDNav.TOKEN_ATTR_NAME :
					return new ValueMatch(
							nav.toString( 
							nav.getAttrVal( //index value to attribute
									nav.toString(index))));				
								
				case VTDNav.TOKEN_CHARACTER_DATA :
				case VTDNav.TOKEN_CDATA_VAL :					
						return new ValueMatch(nav.toString(nav.getText()));
			}
		} catch (NavException e) {
			throw new NavigationException(e);
		} 
		throw new RuntimeException("Invalid type returned: " + type);
		
	}

}
